#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#include "semmun.h"


static int set_semvalue(void);
static void del_semvalue(void);
static int semaphore_p(void);
static int semaphore_v(void);

static int sem_id;

int main(int argc,char * argv[])
{
    int i;
    int pause_time;
    char op_char = 'O';


    sem_id = semget(1234,1,0666|IPC_CREAT);//创建一个信号量
    /*
     *函数原型:int semget(key_t key,int num_sems,int sem_flags);
     * 函数作用：创建一个新信号量或取得一个已有信号量的键
     * 第一个参数是整数值，不相关的进程可以通过它访问同一个信号量。程序对所有信号量的访问都是间接的，它先提供一个键，再由系统生成一个相应的信号量标识符。只有semget函数才直接使用信号量键，所有其他的信号量函数都是使用由semget函数返回的信号量标识符
     * arg2：指定需要的信号量数目。它几乎总是取值为1
     * arg3：是一组标志，它与open函数的标志非常相似。它低端的9个bit是该信号量的权限，其作用类似于文件的访问权限。此外，他们还可以和值IPC_CREAT做按位或操作，来创建一个新信号量。如果函数用不到IPC_CREAT标志，该标志会被悄悄的忽略掉。我们可以通过联合使用标志IPC_CREAT和IPC_EXCL来确保创建出的是一个新的、唯一的信号量。如果该信号量已存在，它将返回一个错误。
     * semget函数在成功时返回一个正数（非零）值，它就是其它信号量函数将用到的信号量标识符，如果失败返回-1
     *
     *
     * */

    if (argc > 1) {
        if (!set_semvalue()) {
            fprintf(stderr,"Failed to initialize semphore");
            exit(EXIT_FAILURE);
        }//初始化一个信号量
        op_char = 'X';
//        sleep(2);
    }
   printf("prepare operating sem.....\n") ;
    for (i = 0; i< 10; i++) {
        if (!semaphore_p()) exit(EXIT_FAILURE);//进行P操作，以至于堵塞其它进程的访问。
        //printf("%c,%d\n",op_char,getpid());
	printf("%c",op_char);fflush(stdout);
/*
        pause_time = rand() % 3;
        sleep(pause_time);

        printf ("%c\n",op_char); fflush(stdout);
*/
     /*   for(;i<(argc +5);i++) {
            printf("[%d] still Running...\n",getpid());    
            sleep(1);
        }*/
        if (!semaphore_v()) exit(EXIT_FAILURE);

        pause_time = rand() %2;
        sleep(pause_time);
    }

    printf ("\n%d - finished\n",getpid());

    if (argc > 1) {
        sleep (10);
        del_semvalue();
    }

    exit(EXIT_SUCCESS);
}

static int set_semvalue(void)
{
    union semun sem_union;

    sem_union.val = 1;
    if (semctl(sem_id,0,SETVAL,sem_union) == -1) return 0;
    /*
     * 函数原型：int semctrl(int sem_id,int sem_num,int command,...);
     * semctl函数用来直接控制信号量信息,初始化一个已知的值，或用于删除一个已经无需继续使用的信号量标识符。
     * arg1：是由semget返回的信号量标识符
     * arg2：是信号量编号，当需要用到成组的信号量时，就要用到这个函数。它一般取0，表示这是第一个也是唯一的一个信号量。
     * arg3：是将要采取的动作。该行为最常用的两个值
         *  SETVAL：用来把信号量初始化为一个已知的值，这个值通过union semun中的val成员设置。其作用是在信号量第一次使用之前对它进行设置。union semun即来自于第四个参数。
         *  IPC_RMID：用于删除一个已经无需继续使用的信号量标识符。
         *
     *函数的返回值根据arg3的不同而返回不同的值，对于SERVAL和IPC_RMID，成功时返回0，失败时返回-1
     *
     * */
    return 1;
}

static void del_semvalue(void)
{
    union semun sem_union;

    if (semctl(sem_id,0,IPC_RMID,sem_union) == -1)
            fprintf(stderr,"Failed to delete semaphore\n");
    
}

static int semaphore_p(void)
{
    struct sembuf sem_b;

    sem_b.sem_num = 0;
    sem_b.sem_op = -1;
    sem_b.sem_flg = SEM_UNDO;
    if (semop(sem_id,&sem_b,1) == -1) {
        fprintf (stderr,"Semaphore_p failed\n");
        return 0;
    }
    /*
     *函数原型：int semop(int sem_id,struct sembuf * sem_ops,size_t num_sem_ops);
     *semop函数用于改变信号量的值
     *arg1：是由semget返回的信号量标识符。
     arg2：是指向一个结构体数组的指针
        * 每个数组元素至少包含以下几个成员：
        * struct sembuf {
        *   short sem_num;
        *   short sem_op;
        *   short sem_flg;  
        * };
        * 第一个成员是信号量编号，除非你需要使用一组信号量，否则它的取值一般为0.
        * sem_op成员的值是信号量中一次操作中需要改变的数值。（你可以使用一个非1的数值来改变信号量的值）。通常只会用到两个值，一个是-1，也就是P操作，它等待信号量变为可用；一个是+1，也就是V操作，表示信号量现在已可用。
        *
        * 最后一个成员sem_flg通常被设置为SEM_UNDO。它将使得操作系统跟踪当前进程对这个信号量的修改情况，如果这个进程在没有释放该信号量的情况下终止，操作系统将自动释放该进程持有的信号量。除非你对信号量的行为有特殊的要求，否则就使用SEM_UNDO，如果不是该值，那就要小心了。
        *
        *
        *
        *
     * */
    return 1;


}

static int semaphore_v(void )
{
    struct sembuf sem_b;

    sem_b.sem_num = 0;
    sem_b.sem_op = 1;
    sem_b.sem_flg = SEM_UNDO;
    if (semop(sem_id,&sem_b,1) == -1) {
        fprintf (stderr,"Semaphore_p failed\n");
        return 0;
    }
    return 1;

}



















